// 版权所有2016 etcd作者
// 
// 根据Apache许可证2.0版（以下简称“许可证”）获得许可；
// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

package ioutil

import (
	"io"
)

var defaultBufferBytes = 128 * 1024

// PageWriter实现io。Writer接口，这样写操作将
// 以页块形式或刷新。
type PageWriter struct {
	w io.Writer
	// pageOffset跟踪缓冲区基址的页偏移量
	pageOffset int
	// pageBytes是每页的字节数
	pageBytes int
	// bufferedBytes统计缓冲区中待写入的字节数
	bufferedBytes int
	// buf持有写入缓冲区
	buf []byte
	// bufWatermarkBytes是字节数缓冲区可以在需要刷新
	// 之前保持。它小于len（buf），因此有空间进行松弛写入
	// 以使写入程序实现页面对齐。
	bufWatermarkBytes int
}

// NewPageWriter创建一个新的PageWriter。pageBytes是每页要写入的字节数
// 。pageOffset是io的起始偏移量。作家
func NewPageWriter(w io.Writer, pageBytes, pageOffset int) *PageWriter {
	return &PageWriter{
		w:                 w,
		pageOffset:        pageOffset,
		pageBytes:         pageBytes,
		buf:               make([]byte, defaultBufferBytes+pageBytes),
		bufWatermarkBytes: defaultBufferBytes,
	}
}

func (pw *PageWriter) Write(p []byte) (n int, err error) {
	if len(p)+pw.bufferedBytes <= pw.bufWatermarkBytes {
		// 无溢出
		copy(pw.buf[pw.bufferedBytes:], p)
		pw.bufferedBytes += len(p)
		return len(p), nil
	}
	// 如果未对齐，则完成缓冲区中的松弛页
	slack := pw.pageBytes - ((pw.pageOffset + pw.bufferedBytes) % pw.pageBytes)
	if slack != pw.pageBytes {
		partial := slack > len(p)
		if partial {
			// 没有足够的数据完成松弛页
			slack = len(p)
		}
		// 特殊情况：写入缓冲区中的松弛页
		copy(pw.buf[pw.bufferedBytes:], p[:slack])
		pw.bufferedBytes += slack
		n = slack
		p = p[slack:]
		if partial {
			// 避免强制未对齐的刷新
			return n, nil
		}
	}
	// 缓冲区内容现在已对齐；清除
	if err = pw.Flush(); err != nil {
		return n, err
	}
	// 直接写入所有完整页面而不复制
	if len(p) > pw.pageBytes {
		pages := len(p) / pw.pageBytes
		c, werr := pw.w.Write(p[:pages*pw.pageBytes])
		n += c
		if werr != nil {
			return n, werr
		}
		p = p[pages*pw.pageBytes:]
	}
	// 将剩余尾部写入缓冲区
	c, werr := pw.Write(p)
	n += c
	return n, werr
}

// 刷新缓冲数据。
func (pw *PageWriter) Flush() error {
	_, err := pw.flush()
	return err
}

// FlushN刷新缓冲数据并返回写入的字节数。
func (pw *PageWriter) FlushN() (int, error) {
	return pw.flush()
}

func (pw *PageWriter) flush() (int, error) {
	if pw.bufferedBytes == 0 {
		return 0, nil
	}
	n, err := pw.w.Write(pw.buf[:pw.bufferedBytes])
	pw.pageOffset = (pw.pageOffset + pw.bufferedBytes) % pw.pageBytes
	pw.bufferedBytes = 0
	return n, err
}
